package com.xz.stu.server.service.impl;


import cn.hutool.json.JSONUtil;
import com.xz.stu.server.entity.*;
import com.xz.stu.server.service.*;
import com.xz.stu.server.vo.grade.GradeByTermVo;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DashboardServiceImpl implements DashboardService {
    @Resource
    private StudentService studentService;
    @Resource
    private EnrollmentService enrollmentService;
    @Resource
    private DepartmentService departmentService;
    @Resource
    private ClassService classService;
    @Resource
    private CourseSelectionService courseSelectionService;
    @Resource
    private StudentCourseService studentCourseService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Map<String, Object> getDashboardSummary() {
        //从缓存获取数据
        String key = "dashboard:summary";
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            String cacheValue = stringRedisTemplate.opsForValue().get(key);
            return JSONUtil.toBean(cacheValue, Map.class);
        }
        Map<String, Object> result = new HashMap<>();
        // 顶部统计卡片数据
        //获取当前学生总数
        List<Student> list = studentService.lambdaQuery()
                .le(Student::getEnrollDate, new Date())
                .ge(Student::getEnrollDate, LocalDate.now().minusYears(4))
                .list();
        int studentCount = list.size();
        //取两位小数

        result.put("studentCount",studentCount);
        result.put("studentGrowth", 5.2);
        //获取当前学生平均成绩
        List<Long> collect = list.stream().map(Student::getId).collect(Collectors.toList());
        List<Enrollment> enrollmentList = enrollmentService.lambdaQuery()
                .in(Enrollment::getStudentId, collect)
                .list();
        //总成绩
        double sum = enrollmentList.stream().mapToDouble(Enrollment::getGrade).sum();
        double avgScore = sum / enrollmentList.size();
        result.put("avgScore",  String.format("%.2f", avgScore));
        result.put("scoreGrowth", 2.3);
        //优秀率
        int yp = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() >= 80).count();
        double excellentRate = (double) yp / enrollmentList.size() * 100;
        result.put("excellentRate", String.format("%.2f", excellentRate));
        result.put("excellentGrowth", 5.2);
        //不及格率
        int jg = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() < 60).count();
        double failRate = (double) jg / enrollmentList.size() * 100;
        result.put("failRate", String.format("%.2f", failRate));
        result.put("failDecline", 1.5);
        //存入缓存
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(result), Duration.ofDays(5));
        return result;
    }

    @Override
    public Map<String, Object> getDepartmentScores(String term) {
        //先从缓存获取数据
        String cacheKey = "dashboard:departmentScores"+term;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(cacheKey))) {
            String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
            return JSONUtil.toBean(cacheValue, Map.class);
        }
        //获取该学期的课程
        List<Long> courseSelectionList = courseSelectionService.lambdaQuery()
                .eq(CourseSelection::getTermId, term)
                .list().stream().map(CourseSelection::getId).collect(Collectors.toList());
        List<Integer> studentCourseList = studentCourseService.lambdaQuery()
                .in(StudentCourse::getCourseSelectId, courseSelectionList)
                .list().stream().map(StudentCourse::getId).collect(Collectors.toList());
        //获取所有课程成绩
        List<Enrollment> enrollmentList = enrollmentService.lambdaQuery()
                .in(Enrollment::getStudentCourseId, studentCourseList)
                .list();

        //各院系平均成绩
        Map<String, Object> result = new HashMap<>();
        List<Department> deptlist = departmentService.list();
        Map<Long, List<StuClass>> classMap = classService.list().stream().collect(Collectors.groupingBy(StuClass::getDeptId));
        List<Student> stuList = studentService.lambdaQuery()
                .le(Student::getEnrollDate, new Date())
                .ge(Student::getEnrollDate, LocalDate.now().minusYears(4))
                .list();
        //按院系区分学生
        HashMap<Department, List<Long>> map = new HashMap<>();
        for (Department dept : deptlist) {
            List<Long> stuClasses = classMap.get(dept.getId()).stream().map(StuClass::getId).collect(Collectors.toList());
            List<Long> studentList = stuList.stream().filter(stu -> stuClasses.contains(stu.getClassId())).map(Student::getId).collect(Collectors.toList());
            map.put(dept, studentList);
        }
        // 各院系平均成绩
        ArrayList<String> scores = new ArrayList<>();
        List<Department> deptNames = new ArrayList<>();
        map.forEach((dept, studentList) -> {
            enrollmentList.stream().filter(enrollment -> studentList.contains(Long.valueOf(enrollment.getStudentId())))
                    .mapToDouble(Enrollment::getGrade)
                    .average()
                    .ifPresent(s->{
                        scores.add(String.format("%.2f",s));
                    });
            deptNames.add(dept);
        });
        // 院系成绩对比数据
        result.put("scores", scores);
        result.put("deptNames", deptNames);
        stringRedisTemplate.opsForValue().set(cacheKey,JSONUtil.toJsonStr(result), Duration.ofDays(5));
        return result;
    }

    @Override
    public Map<String, Object> getScoreDistribution(String term) {
        //先从缓存获取数据
        String cacheKey = "dashboard:scoreDistribution"+term;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(cacheKey))) {
            String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
            return JSONUtil.toBean(cacheValue, Map.class);
        }
        Map<String, Object> result = new HashMap<>();
        List<Student> list = studentService.lambdaQuery()
                .le(Student::getEnrollDate, new Date())
                .ge(Student::getEnrollDate, LocalDate.now().minusYears(4))
                .list();
        List<Long> collect = list.stream().map(Student::getId).collect(Collectors.toList());
        List<Enrollment> enrollmentList = enrollmentService.lambdaQuery()
                .in(Enrollment::getStudentId, collect)
                .list();
        // 成绩分布数据
        List<Map<String, Object>> distribution = new ArrayList<>();
        //优秀(90+)
        int yp = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() >= 90).count();
        distribution.add(createDistributionItem(yp, "优秀(90+)"));
        //良好(80-89)
        int gl = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() >= 80 && enrollment.getGrade() < 90).count();
        distribution.add(createDistributionItem(gl, "良好(80-89)"));
        //中等(70-79)
        int mq = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() >= 70 && enrollment.getGrade() < 80).count();
        distribution.add(createDistributionItem(mq, "中等(70-79)"));
        //及格(60-69)
        int jg = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() >= 60 && enrollment.getGrade() < 70).count();
        distribution.add(createDistributionItem(jg, "及格(60-69)"));
        //不及格(<60)
        int jb = (int) enrollmentList.stream().filter(enrollment -> enrollment.getGrade() < 60).count();
        distribution.add(createDistributionItem(jb, "不及格(<60)"));
        result.put("distribution", distribution);
        //加入缓存
        stringRedisTemplate.opsForValue().set(cacheKey,JSONUtil.toJsonStr(result), Duration.ofDays(5));
        return result;
    }

    @Override
    public Map<String, Object> getTrendData() {
        String cacheKey = "dashboard:trendData";
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(cacheKey))) {
            String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);
            return JSONUtil.toBean(cacheValue, Map.class);
        }
        List<GradeByTermVo> enrollmentList  =enrollmentService.getGradeByTerm();
        Map<String, Object> result = new HashMap<>();
        // 学期趋势数据
        Map<String, List<GradeByTermVo>> listMap = enrollmentList.stream().collect(Collectors.groupingBy(GradeByTermVo::getTermName));
        List<String> scores = new ArrayList<>();
        List<String> excellentRate = new ArrayList<>();
        List<String> failRate = new ArrayList<>();
        List<String> terms = new ArrayList<>();
        listMap.forEach((term, gradeByTermVos) -> {
            //平均成绩
            double avg = gradeByTermVos.stream().mapToDouble(GradeByTermVo::getGrade).average().orElse(0);
            //优秀率
            double excel= (double) gradeByTermVos.stream()
                    .filter(gradeByTermVo -> gradeByTermVo.getGrade() >= 90).count() / gradeByTermVos.size() * 100;
            //不及格率
            double rate = (double) gradeByTermVos.stream()
                    .filter(gradeByTermVo -> gradeByTermVo.getGrade() < 60).count() / gradeByTermVos.size() * 100;
            scores.add(String.format("%.2f",avg));
            excellentRate.add(String.format("%.2f",excel));
            failRate.add(String.format("%.2f",rate));
            terms.add(term);
        });

        result.put("scores", scores);
        result.put("excellentRates", excellentRate);
        result.put("failRates", failRate);
        result.put("terms", terms);
        stringRedisTemplate.opsForValue().set(cacheKey,JSONUtil.toJsonStr(result), Duration.ofDays(5));
        return result;
    }

    private Map<String, Object> createDistributionItem(int value, String name) {
        Map<String, Object> item = new HashMap<>();
        item.put("value", value);
        item.put("name", name);
        return item;
    }

}
